/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 *
 */
#include <algorithm>
#include <cstring>

#include "base/types.hh"
#include "config/the_isa.hh"
#include "arch/isa_traits.hh"
#include "cpu/edge/atomic/atomic_block.hh"
#include "base/bitfield.hh"
#include "base/trace.hh"
#include "base/loader/symtab.hh"


template<class Impl>
AtomicEdgeBlock<Impl>::AtomicEdgeBlock(BlockID blockID, CPU* cpu)
    : BaseAtomicEdgeBlock<Impl>(blockID, cpu)
{
    _mark = 0;
    _type = 0;
    _xFlags = 0;
    _sMask = 0;
    _chunkNum = 0;

    _sMaskFlag.reset();

    branchTarget = 0;

    _numReceivedStore = 0;
    _numReceivedWrite = 0;
    _numReceivedExit = 0;

    executedInsts = 0;    
}

template<class Impl>
AtomicEdgeBlock<Impl>::~AtomicEdgeBlock(){}

template<class Impl>
std::string
AtomicEdgeBlock<Impl>::name() const
{
    return this->cpu->name() + ".EdgeInstBlock";
}

template<class Impl>
bool
AtomicEdgeBlock<Impl>::depackHeader(HeaderInfo& info)
{
    assert(info.size() >= TheISA::HeaderInfoSize);
    _mark = bits(info[3], 31, 24 );
    _type = bits(info[3], 23, 16);
    _xFlags = bits( info[3], 15, 8 );
    _sMask = info[1];
    _chunkNum = ( _type == 0 ) ? 4 : _type;
    
    if(_chunkNum > 4)
        _chunkNum = 1;
        
    // As we know how many chunks this block has we can set the next
    // block PC in program order
    nextBlockPC = this->startAddr + ( _chunkNum + 1) * TheISA::ChunkSize;
    
    // Currently do nothing but dprint it
    DPRINTF(AtomicEdgeCPU, "\nBlockID %d -- Block StartAddress 0x%#x.\n",
                               this->blockID, this->startAddr);
    DPRINTF (AtomicEdgeCPU, "Header MARK : 0x%x\nHeader TYPE : 0x%x\nHeader XFLAGS : 0x%x\n"
                "Header SMASK : 0x%x\n", _mark, _type, _xFlags, _sMask);

    if ( _mark != 0xff ) {
        return false;
    } 

    return true;

}

template<class Impl>
bool
AtomicEdgeBlock<Impl>::isCompletionCondSatisfied()
{
    DPRINTF(AtomicEdgeCPU, "Block[id:%lli] has received %i writes, %i stores and %i exits.\n",
        this->getBlockID(),
        _numReceivedWrite,
        _numReceivedStore,
        _numReceivedExit );

    return (this->getNumRegWriteInst() == _numReceivedWrite ) &&
            (_sMask == _sMaskFlag.to_ulong()) &&
            (_numReceivedExit == 1);
}

template<class Impl>
void
AtomicEdgeBlock<Impl>::setBranchTarget( Addr branch_pc )
{
    // Make sure this is an chunk-aligned address
    // Sometimes a mispredicted series of blocks will
    // set the branch target to silly value. This 
    // will not affect the output due to 
    // misprediction handling methods.
    //assert( (branch_pc & TheISA::ChunkOffset) == 0);

    branchTarget = branch_pc;
}

template<class Impl>
TheISA::LsID
AtomicEdgeBlock<Impl>::getLastStore( TheISA::LsID lsid )
{
    assert( lsid < TheISA::StoreMaskNum );

    DPRINTF(EdgeBlock, "Get last store for LSID %i\n", lsid);

    if ( lsid == 0 ) return -1;

    while ( !bits(_sMask, --lsid) ) {
        if (lsid == 0 ) return -1;
    }

    DPRINTF(EdgeBlock, "Last store is LSID %i\n", lsid);

    return lsid;
}

template<class Impl>
TheISA::LsID
AtomicEdgeBlock<Impl>::getMemBlockerLSID()
{
    assert(_sMask > 0);

    uint32_t status = _sMask ^ ((uint32_t)_sMaskFlag.to_ulong());

    TheISA::LsID blocker_lsid = findLsbSet(status);

    return blocker_lsid;
}

template<class Impl>
void
AtomicEdgeBlock<Impl>::recordExitType(bool isCall, bool isReturn, bool isIndirect)
{
    assert(!(isCall & isReturn));

    if (isCall) {
        exitType = TheISA::call;
    } else if (isReturn) {
        exitType = TheISA::ret;
    } else if (branchTarget == nextBlockPC) {
        exitType = TheISA::seq;
    } else {
        exitType = TheISA::branch;
    }
}

